package top.z.work.framework.common.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.LongSerializationPolicy;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public final class GsonUtil {

    private static final Gson GSON;

    static {
        GSON = new GsonBuilder()
                // 当字段值为空或null时, 依然对该字段进行转换
                .serializeNulls()
                // 防止特殊字符出现乱码
                .disableHtmlEscaping()
                // long类型序列化为string
                .setLongSerializationPolicy(LongSerializationPolicy.STRING)
                // LocalDate序列化适配器
                .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter())
                // LocalDateTime序列化适配器
                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTypeAdapter())
                // 序列化日期格式
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                //
                .create();
    }

    // 获取GSON解析器
    public static Gson gson() {
        return GSON;
    }

    /**
     * 根据对象返回json过滤空值字段
     * Type type = new TypeToken<List<User>>() {}.getType();
     */
    public static <T> String toJson(T data) {
        return GSON.toJson(data);
    }

    /**
     * 根据对象返回json过滤空值字段
     * Type type = new TypeToken<List<User>>() {}.getType();
     */
    public static <T> String toJson(T data, Type type) {
        return GSON.toJson(data, type);
    }

    /**
     * 将字符串转化对象
     *
     * @param json  源字符串
     * @param clazz 目标对象类型
     * @param <T>   对象
     * @return T
     */
    public static <T> T toBean(String json, Class<T> clazz) {
        return GSON.fromJson(json, clazz);
    }

    /**
     * 将json转化为对应的实体对象
     * new TypeToken<List<T>>() {}.getType()
     * new TypeToken<Map<String, T>>() {}.getType()
     * new TypeToken<List<Map<String, T>>>() {}.getType()
     */
    public static <T> T toBean(String json, Type typeOfT) {
        return GSON.fromJson(json, typeOfT);
    }

}

/**
 * Gson格式化LocalDateTime适配器
 */
@Slf4j
class LocalDateTimeTypeAdapter extends TypeAdapter<LocalDateTime> {
    @Override
    public void write(JsonWriter jsonWriter, LocalDateTime value) throws IOException {
        if (value == null) {
            jsonWriter.nullValue();
        } else {
            jsonWriter.value(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(value));
        }
    }

    @Override
    public LocalDateTime read(JsonReader jsonReader) throws IOException {
        if (jsonReader.peek() == JsonToken.NULL) {
            jsonReader.nextNull();
            return null;
        } else {
            return LocalDateTime.parse(jsonReader.nextString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
    }
}

/**
 * Gson格式化LocalDateTime适配器
 */
@Slf4j
class LocalDateTypeAdapter extends TypeAdapter<LocalDate> {
    @Override
    public void write(final JsonWriter jsonWriter, final LocalDate value) throws IOException {
        if (value == null) {
            jsonWriter.nullValue();
        } else {
            jsonWriter.value(DateTimeFormatter.ofPattern("yyyy-MM-dd").format(value));
        }
    }

    @Override
    public LocalDate read(final JsonReader jsonReader) throws IOException {
        if (jsonReader.peek() == JsonToken.NULL) {
            jsonReader.nextNull();
            return null;
        } else {
            return LocalDate.parse(jsonReader.nextString(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }
    }
}
